#!../ext/bin/ruby
# Copyright Bjorn De Meyer 2007. Can be used freely under the zlib license.
#
#

# How to use this map editor:
# scroll mouse wheel is select tile
# right click for making selections
# left click for ptting a tile
# L for adding a new layer
# + - for moving between the layers
# F2 for save
# F10 for save and quit
# C for copy selection
# P for paste selection
# W,U,D,S,D,J,L for setting the tile 's flags

BEGIN { $:.unshift(File.dirname(__FILE__)); }
# Include this script's directory in Ruby's search path.


require 'rogaru'
include Rogaru

END { Rubygame.quit() }
# Ensure return to normal resolution.

def screen_make(wide, high, fullscreen, doublebuf = true) 
	flags  = [ Rubygame::HWSURFACE , Rubygame::ANYFORMAT ]
	flags << Rubygame::FULLSCREEN if fullscreen
	flags << Rubygame::DOUBLEBUF  if doublebuf
	begin
		screen = Rubygame::Screen.new( [640, 480], 16, flags )
	rescue Rubygame::SDLError
		raise "Could not open screen."
	end
	screen.title = 'Eruma'
	return screen
end 

def event_setup()
	queue   = Rubygame::EventQueue.new() { @autofetch = true }
	return queue
end


class Selection
	attr_accessor :top
	attr_accessor :left
	attr_accessor :bottom
	attr_accessor :right

	def initialize()
		@stage  = 0
		@buffer = nil
	end

	def set_first(x, y)
		@top 	= y
		@left 	= x
		@stage  = 1
	end
	
	def set_second(x, y)
		@bottom	= y
		@right 	= x
		@stage  = 2
	end
	
	def selected?
		return @stage == 2
	end	

	def started?
		return @stage == 1
	end	

	def empty?
		return @stage == 0
	end	

	def clear!
		@stage = 0
	end	

	def fill(map, edit_layer, edit_tile)
		return false unless (self.selected?)
		for i in (@left..@right) do
			for j in (@top..@bottom) do
				map.set(i, j, edit_layer, edit_tile)
			end
		end
	end

	def copy(map, edit_layer)
		return false unless (self.selected?)
		@buffer = []
		for y in (@top..@bottom) do
			line = []
			for x in (@left..@right) do
 				tile = map.get(x, y, edit_layer)
				line.push tile
			end
			@buffer.push line
		end
	end

	def paste(map, edit_x, edit_y, edit_layer)
		return false unless (self.selected?)
		return false if @buffer.nil?
		stopy = @buffer.size
		for y in (0...stopy) do
			line = @buffer[y]
			stopx = line.size
			for x in (0...stopx) do
 				tile = line[x]
				begin 
					map.set(x + edit_x, y + edit_y, edit_layer, tile)
				rescue
					# Don't care if out of range, just don't paste there.
				end
			end
		end
	end


	def to_s
		return 'none' if @stage == 0
		return "#{@left},#{@top}:?,?" if @stage ==1
		return "#{@left},#{@top}:#{@right},#{@bottom}" if @stage == 2
		return 'Should not happen.'
	end
end

def calculate_map_speed(screen, map, speed, mouse_x,  mouse_y)
	if mouse_x < 20 then
		map.vx = -speed 
	elsif mouse_x > screen.w - 20
		map.vx = speed 
	else
		map.vx = 0
	end
	
	if mouse_y < 20 then
		map.vy = -speed 
	elsif mouse_y > screen.h - 20
		map.vy = speed 
	else
		map.vy = 0
	end 
end

def mark_active_tile(screen, map, edit_x, edit_y, edit_layer, tilewide)
		x1 = map.tile_to_screen_x(edit_layer, edit_x)
		y1 = map.tile_to_screen_y(edit_layer, edit_y)
		screen.draw_box( [x1 , y1], [ (x1+tilewide), (y1 + tilewide) ], [255,255,255])
end

def mark_active_selection(screen, map, edit_select, edit_layer, tilewide)
		return false unless edit_select.selected?
	 	x1 = map.tile_to_screen_x(edit_layer, edit_select.left)
		x2 = map.tile_to_screen_x(edit_layer, edit_select.right + 1)
	 	y1 = map.tile_to_screen_y(edit_layer, edit_select.top)
		y2 = map.tile_to_screen_y(edit_layer, edit_select.bottom + 1) 
		screen.draw_box( [x1, y1], 	[ x2, y2], [255,100,100]) 
		return true
end

def main(filename, wide=100, high=100, tilewide = 40, tilehigh = 40, fullscreen = false)
	speed 		= 10
	edit_layer  = 0
	edit_x      = 0
	edit_y      = 0
	edit_tile   = 0
	edit_bg 	= true
	edit_select = Selection.new()
	
	queue 		= event_setup()
	screen  	= screen_make(640, 480, fullscreen, true)
	tile_dir 	= "../share/image/tile/"
	Tilemap::Tile.set_tile_directory(tile_dir); 
	
	if(File.exist?(filename))
		begin 
			map = Tilemap::Map.load_from(filename)
		rescue
			raise "Could not load map from {filename}: " + $!
		end
	else
		map 		= Tilemap::Map.new()
		map.new_layer(wide, high, tilewide, tilehigh) 
	end		
	
	clock       = Rubygame::Clock.new()
	ttf 		= Rubygame::TTF
	ttf.setup()
	font 		= ttf.new("../share/font/crystal.ttf", 14)
	 
	done 		= false

	until done do
		screen.fill([255, 0, 255]) if edit_bg
        map.draw_auto_with_effects(screen)
		mark_active_tile(screen, map, edit_x, edit_y, edit_layer, tilewide)	
		mark_active_selection(screen, map, edit_select, edit_layer, tilewide)
		status = "Speed: #{speed}. XYZ: #{edit_x}, #{edit_y}, #{edit_layer}/#{map.layer_count-1}"   
		status += ". Tile: #{edit_tile}. Select: #{edit_select}"
		UI::Text.prepare_and_draw_text(screen, [10, 10], status, 500, font, [255,255,255])
		screen.flip()
		queue.each do 	
			| event | 
			if(event.is_a? Rubygame::QuitEvent) then 
				done = true
			elsif (event.is_a? Rubygame::KeyDownEvent) then 
				if event.key == Rubygame::K_L
					puts "Map new layer: #{map.tw}, #{map.th}"
					map.new_layer(map.tw, map.th) 
				end
				# Add new Layer.
				edit_select.copy(map, edit_layer) if event.key == Rubygame::K_C
				# Copy selection.
				edit_select.paste(map, edit_x, edit_y, edit_layer) if event.key == Rubygame::K_P
				# Paste selection.
				edit_tile = map.get(edit_x, edit_y, edit_layer) if event.key == Rubygame::K_F5
				# Set the active tile to the one currently under the mouse pointer
				done = true if event.key == Rubygame::K_F10
				# Exit with save.
				done = true if event.key == Rubygame::K_ESCAPE
				# Exit without save.
			
				edit_bg = (not edit_bg) if event.key == Rubygame::K_B
				# Toggle drawing of simple bright pink background
				if event.key == Rubygame::K_W
					tile_i = map.get(edit_x, edit_y, edit_layer)
					tile = map.get_tile(tile_i)
					tile.walk = (not tile.walk)	unless tile.nil?
				end
				# Toggle walkability of tile under cursor and all similar tiles
				if event.key == Rubygame::K_S
					tile_i = map.get(edit_x, edit_y, edit_layer)
					tile = map.get_tile(tile_i)
					tile.swim = (not tile.swim)	unless tile.nil?
				end
				# Toggle swimmable property of tile
				if event.key == Rubygame::K_J
					tile_i = map.get(edit_x, edit_y, edit_layer)
					tile = map.get_tile(tile_i)
					tile.jump = (not tile.jump)	unless tile.nil?
				end
				# Toggle jump property of tile
				if event.key == Rubygame::K_U
					tile_i = map.get(edit_x, edit_y, edit_layer)
					tile = map.get_tile(tile_i)
					tile.up = (not tile.up)	unless tile.nil?
				end
				# Toggle Up property of tile
				if event.key == Rubygame::K_D
					tile_i = map.get(edit_x, edit_y, edit_layer)
					tile = map.get_tile(tile_i)
					tile.down = (not tile.down)	unless tile.nil?
				end
				# Toggle Down property of tile
				if event.key == Rubygame::K_O
					tile_i = map.get(edit_x, edit_y, edit_layer)
					tile = map.get_tile(tile_i)
					tile.pain = (not tile.pain)	unless tile.nil?
				end
				# Toggle Ouch property of tile, that is pain
				if event.key == Rubygame::K_L
					tile_i = map.get(edit_x, edit_y, edit_layer)
					tile = map.get_tile(tile_i)
					tile.ledge = (not tile.ledge) unless tile.nil?
				end
				# Toggle Ledge property of tile
 				speed += 5 if event.key == Rubygame::K_KP_MULTIPLY and speed < 100
				# Speed of scrolling up
 				speed -= 5 if event.key == Rubygame::K_KP_DIVIDE and speed > 5
				# Speed of scrolling down
				edit_layer += 1 if event.key == Rubygame::K_KP_PLUS and edit_layer  < (map.layer_count-1)
				# Up to next layer
				edit_layer -= 1 if event.key == Rubygame::K_KP_MINUS and edit_layer > 0
				# Down to previous layer
				if event.key == Rubygame::K_F2  or event.key == Rubygame::K_F10 then
				# Save map
					UI::Text.prepare_and_draw_text(screen, [10, 10], 
					"Saving to #{filename}...", 500, font, [255,255,255])
					screen.flip()
					map.save_to(filename)
					sleep 1
				end
			elsif (event.is_a? Rubygame::MouseDownEvent) then 
				edit_tile +=1 if event.button == 5 and edit_tile < (map.tile_count-1)
				edit_tile -=1 if event.button == 4 and edit_tile >= 0 
				# Negative tile index erases to transparency
				if event.button == 1 or event.button == 4 or event.button == 5 then
					if(edit_select.selected?)
						edit_select.fill(map, edit_layer, edit_tile)
					else
						map.set(edit_x, edit_y, edit_layer, edit_tile) 
					end
				elsif event.button == 3  then
					if(edit_select.empty?) 
						edit_select.set_first(edit_x, edit_y)
					elsif(edit_select.started?) 
						edit_select.set_second(edit_x, edit_y)
					else
						edit_select.clear!  # remove selection
					end
				end
			elsif (event.is_a? Rubygame::MouseUpEvent) then 
			elsif(event.is_a? Rubygame::MouseMotionEvent) then 
				mouse_x = event.pos[0]
				mouse_y = event.pos[1]
				edit_x  = map.screen_to_tile_x(edit_layer, mouse_x)
				edit_y  = map.screen_to_tile_y(edit_layer, mouse_y)
				calculate_map_speed(screen, map, speed, mouse_x, mouse_y)
				# The function calculate_map_speed allows to scroll the map with the mouse.
			end
		end
		map.advance
		# Animate map tiles.
		map.update(screen.w, screen.h)
		# Move map to correct position, limited to these lower corners.
		clock.tick
	end
	stop 	= Time.now()
	fps    = clock.framerate
	puts "Average FPS: #{fps}\n"
	return 0
end


filename = ARGV[0]

if filename.nil? then
	puts "eruba.rb map_file_name [width in tiles] [height in tiles] [tile wide] [tile high]"
else
	
wide 		= 50
high 		= 50
tilewide 	= 40
tilehigh 	= 40

wide 		= ARGV[1].to_i if ARGV.size > 1
high 		= ARGV[2].to_i if ARGV.size > 2
tilewide 	= ARGV[3].to_i if ARGV.size > 3
tilehigh	= ARGV[4].to_i if ARGV.size > 4

main(filename, wide, high, tilewide, tilehigh, false)

end